/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package scriptedsands;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Iterator;

/**
 * Super class of all engines. An engine must be a subclass of this to work.
 *
 * @author Dalton
 */
public abstract class MappedEngine extends AbstractEngine {
    public Particle[][] sandbox;
    public Particle currentElem;
    /*
    protected int width = 512;
    protected int height = 512;
    protected BufferedImage bImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    protected boolean isPaused;
    protected boolean isDrawing;
    protected long fps;
    protected int penSize;
    protected int oldMouseX, oldMouseY, newMouseX, newMouseY;*/

    public void setup() {
        sandbox = new Particle[512][512];
        isPaused = false;

        for (int r = 0; r < sandbox.length; r++) {
            for (int c = 0; c < sandbox[r].length; c++) {
                putElement(Particle.EMPTY, r, c);
            }
        }
    }

    public abstract void draw();

    public abstract void step();

    public abstract void putElement(Particle elem, int x, int y);

    public abstract void putCurrentElement(int x, int y);

    public abstract void moveElement(int x1, int y1, int x2, int y2);

    public abstract void clearSandbox();

    public void setElement(Object e) {
        currentElem = (Particle) e;
    }

    public void drawElement(Particle e, int x, int y) {
        bImg.setRGB(x, y, e.getColor().getRGB());
    }

    public void drawLineFilled(Particle elem) {
        drawLineFilled(elem, oldMouseX, oldMouseY, newMouseX, newMouseY, penSize);
    }

    public void drawLineFilled(Particle elem, int x1, int y1, int x2, int y2, int width) {
        if (x1 == x2 && y1 == y2) {
            drawCircleFilled(elem, x1, y1, width);
        }
        else {
            ArrayList<Point> circlePoints1 = getCirclePoints(x1, y1, width);
            ArrayList<Point> circlePoints2 = getCirclePoints(x2, y2, width);
            Iterator<Point> iter1 = circlePoints1.iterator();
            Iterator<Point> iter2 = circlePoints2.iterator();
            Point point1, point2;
            while (iter1.hasNext() && iter2.hasNext()) {
                point1 = iter1.next();
                point2 = iter2.next();
                drawLine(elem, point1.x, point1.y, point2.x, point2.y);
                drawLine(elem, point1.x, point1.y+1, point2.x, point2.y+1);
            }
        }
    }

    public void drawLine(Particle elem, int x1, int y1, int x2, int y2) {
        boolean steep = Math.abs(y2 - y1) > Math.abs(x2 - x1);
        if (steep) {
            int temp = x1;
            x1 = y1;
            y1 = temp;
            temp = x2;
            x2 = y2;
            y2 = temp;
        }
        if (x1 > x2) {
            int temp = x1;
            x1 = x2;
            x2 = temp;
            temp = y1;
            y1 = y2;
            y2 = temp;
        }
        int deltax = x2 - x1;
        int deltay = Math.abs(y2 - y1);
        int error = deltax / 2;
        int ystep;
        int y = y1;
        if (y1 < y2)
            ystep = 1;
        else
            ystep = -1;
        for (int x = x1; x < x2; x++) {
            if (steep)
                putElement(elem, y, x);
            else
                putElement(elem, x, y);
            error -= deltay;
            if (error < 0) {
                y += ystep;
                error += deltax;
            }
        }
    }

    private void drawCircle(Particle elem, int cx, int cy) {
        int error = -penSize;
        int x = penSize;
        int y = 0;
        while( x >= y) {
            plot8Points(elem, cx, cy, x, y);

            error += y;
            y++;
            error += y;

            if (error >= 0) {
                x--;
                error -= x;
                error -= x;
            }
        }
    }

    private void drawCircleFilled(Particle elem, int cx, int cy, int radius) {
        int error = -radius;
        int x = radius;
        int y = 0;
        while( x >= y) {
            plotLines(elem, cx, cy, x, y);
            plotLines(elem, cx, cy, y, x);

            error += y;
            y++;
            error += y;

            if (error >= 0) {
                x--;
                error -= x;
                error -= x;
            }
        }
    }

    private void plot8Points(Particle elem, int cx, int cy, int x, int y) {
        plot4Points(elem, cx, cy, x, y);
        if (x != y) plot4Points(elem, cx, cy, y, x);
    }

    private void plot4Points(Particle elem, int cx, int cy, int x, int y) {
        putElement(elem, cx + x, cy + y);
        if (x != 0) putElement(elem, cx - x, cy + y);
        if (y != 0) putElement(elem, cx + x, cy - y);
        if (x != 0 && y != 0) putElement(elem, cx - x, cy - y);
    }

    private void plotLines(Particle elem, int cx, int cy, int x, int y) {
        drawLine(elem, cx+x,cy+y, cx-x,cy+y);
        drawLine(elem, cx+x,cy-y, cx-x,cy-y);
    }

    private ArrayList<Point> getCirclePoints(int cx, int cy, int radius) {
        ArrayList<Point> points = new ArrayList<Point>();
        int error = -radius;
        int x = radius;
        int y = 0;
        while( x >= y) {
            points.addAll(get8Points(cx, cy, x, y));
            points.addAll(get8Points(cx, cy, y, x));

            error += y;
            y++;
            error += y;

            if (error >= 0) {
                x--;
                error -= x;
                error -= x;
            }
        }
        return points;
    }

    private ArrayList<Point> get8Points(int cx, int cy, int x, int y) {
        ArrayList<Point> points = new ArrayList<Point>();
        points.addAll(get4Points(cx, cy, x, y));
        if (x != y) {
            points.addAll(get4Points(cx, cy, y, x));
        }
        return points;
    }

    private ArrayList<Point> get4Points(int cx, int cy, int x, int y) {
        ArrayList<Point> points = new ArrayList<Point>();
        putCurrentElement(cx + x, cy + y);
        points.add(new Point(cx+x, cy+y));
        if (x != 0) {
            putCurrentElement(cx - x, cy + y);
            points.add(new Point(cx-x, cy+y));
        }
        if (y != 0) {
            putCurrentElement(cx + x, cy - y);
            points.add(new Point(cx+x, cy-y));
        }
        if (x != 0 && y != 0) {
            putCurrentElement(cx - x, cy - y);
            points.add(new Point(cx-x, cy-y));
        }
        return points;
    }
}
